package com.yox.rpc.transport.socket.util;

import com.yox.rpc.entity.RpcRequest;
import com.yox.rpc.enumeration.PackageType;
import com.yox.rpc.serializer.CommonSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;

/**
 * @description Socket方式将数据按照 CommonEncoder 协议编码并序列化写入输出流中
 *
 * +---------------+---------------+-----------------+-------------+
 * |  Magic Number |  Package Type | Serializer Type | Data Length |
 * |    4 bytes    |    4 bytes    |     4 bytes     |   4 bytes   |
 * +---------------+---------------+-----------------+-------------+
 * |                          Data Bytes                           |
 * |                   Length: ${Data Length}                      |
 * +---------------------------------------------------------------+
 * 首先是 4 字节魔数，表识一个协议包。接着是 Package Type，标明这是一个调用请求还是调用响应，Serializer Type 标明了实际数据使用的序列化器，
 * 这个服务端和客户端应当使用统一标准；Data Length 就是实际数据的长度，设置这个字段主要防止粘包，
 * 最后就是经过序列化后的实际数据，可能是 RpcRequest 也可能是 RpcResponse 经过序列化后的字节，取决于 Package Type。
 *
 */
public class ObjectWriter {

    private static final Logger logger = LoggerFactory.getLogger(ObjectWriter.class);
    private static final int MAGIC_NUMBER = 0xCAFEBABE;

    /**
     *  将 object 使用 serializer 序列化器进行序列化
     * @param out
     * @param object
     * @param serializer
     * @throws IOException
     */
    public static void writeObject(OutputStream out, Object object, CommonSerializer serializer) throws IOException {
        // 写入魔数
        out.write(intToBytes(MAGIC_NUMBER));

        // 写入数据包类型
        if(object instanceof RpcRequest) {
            out.write(intToBytes(PackageType.REQUEST_PACK.getCode()));
        } else {
            out.write(intToBytes(PackageType.RESPONSE_PACK.getCode()));
        }

        // 写入序列化器编号
        out.write(intToBytes(serializer.getCode()));
        // 使用序列化器进行序列化
        byte[] bytes = serializer.serialize(object);
        // 写入数据长度
        out.write(intToBytes(bytes.length));
        // 写入数据
        out.write(bytes);
        out.flush();
    }

    /**
     * @description 将Int转换为字节数组
     * @param value int类型值
     * @return [byte[]]
     */
    private static byte[] intToBytes(int value) {
        byte[] src = new byte[4];
        src[0] =  (byte) ((value>>24) & 0xFF);
        src[1] =  (byte) ((value>>16) & 0xFF);
        src[2] =  (byte) ((value>>8) & 0xFF);
        src[3] =  (byte) (value & 0xFF);
        return src;
    }
}

